package XMFramework.Core.Internal
{
	import GTweener.TweenLite;
	
	import XMFramework.Interface.IResources;
	
	import flash.display.Loader;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.NetStatusEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLRequest;
	import flash.system.ApplicationDomain;
	import flash.system.LoaderContext;
	import flash.utils.setTimeout;
	
	[Event(name="SResourceLoader_LOAD_COMPLETED", type="SResourceLoader")]
	
	public class SResourceLoader extends EventDispatcher
	{
		public static const LOAD_COMPLETED : String			=	 "SResourceLoader_LOAD_COMPLETED";		
		private static const CALLBACK_FUNCTION : String		=	 "Function";		
		private static const CALLBACK_FUNCTION_EX : String	=	 "FunctionEx";		
		private static const CALLBACK_OBJECT : String		=	 "Object";
		
			
		private var name : String;			
		private var priority : String;
		private var resURL : String;
			
		private var resourceData : ApplicationDomain	=	null;	// List LoaderInfo data
		private var loader : Loader;
		private var isLoading : Boolean	=	false;		
		private var totalByte:uint		=	0;		
		private var loadedByte:uint		=	0;
		private var targetList : Array	=	new Array();		
			
		public var onLoadResultFunc : Function	=	null;
			
		public function SResourceLoader( name:String, url:String, priority:String )
		{
			this.name		=	name;
			this.priority	=	priority;
			this.resURL		=	url;
			
			super();
		}		
		
		public function loadResource() : void
		{				
			if( isLoading )
				return;
			
			if( loader != null )
			{
				loader.unload();
				loader			=	null;				
				resourceData	=	null;
			}
			
			var context : LoaderContext	=	new LoaderContext();
			context.applicationDomain	=	ApplicationDomain.currentDomain;	// Loader's own ApplicationDomain
			context.securityDomain		=	null;
			
			loader	=	new Loader();
			
			var dispatcher : IEventDispatcher	=	loader.contentLoaderInfo;
			dispatcher.addEventListener( Event.COMPLETE, onSWFLoadComplete );
			dispatcher.addEventListener( IOErrorEvent.IO_ERROR, onSWFLoadError );
			dispatcher.addEventListener( SecurityErrorEvent.SECURITY_ERROR, onSWFLoadSecurityError );
			dispatcher.addEventListener( NetStatusEvent.NET_STATUS, onNetStatusHandler );
			dispatcher.addEventListener( ProgressEvent.PROGRESS, onLoadPercentage );
			loader.load( new URLRequest( resURL ), context );
			
			isLoading	=	true;
		}
					
		public function getClass( className : String, obj : IResources ) : void
		{
			if( resourceData && resourceData.hasDefinition( className ) )
			{
				if( resourceData.hasDefinition( className ) )
				{
					var cls : Class	=	resourceData.getDefinition( className ) as Class;
					obj.onLoadComplete( name, className, cls );
				}
				else
					trace("SResourceLoader::getClass class '"+className+"' not found" );
			}
			
			else
			{
				var targetObj : Object	=	new Object();
				targetObj.className	=	className;
				targetObj.target	=	obj;
				targetObj.type		=	CALLBACK_OBJECT;
				
				targetList.push( targetObj );
				
				loadResource();
			}
		}	
		
		public function getClassFunc( className : String, func : Function, thisObject : * ) : void
		{
			if( resourceData )
			{
				if( resourceData.hasDefinition( className ) )
				{
					var cls : Class	=	resourceData.getDefinition( className ) as Class;					
					//GTweener.TweenLite.delayedCall( 1, func, [name, className, cls], true );
					func.call( null, name, className, cls );
				}
				else
					trace("SResourceLoader::getClassFunc class '"+className+"' not found" );
			}
			
			else
			{
				var targetObj : Object	=	new Object();
				targetObj.className		=	className;
				targetObj.target		=	func;
				targetObj.type			=	CALLBACK_FUNCTION;
				targetObj.thisObject	=	thisObject;
				
				targetList.push( targetObj );
				
				loadResource();
			}
		}
		
		public function getImmediateClassFunc( className : String ) : Class
		{
			if( resourceData )
			{
				if( resourceData.hasDefinition( className ) )	
				{
					var cls : Class	=	resourceData.getDefinition( className ) as Class;
					return cls;
				}
				else
					trace("SResourceLoader::getImmediateClassFunc class '"+className+"' not found" );
			}
			
			return XFLoadingIcon;	
		}		
		
		public function getClassFuncEx( className : String, func : Function, thisObject : *, ...params ) : void
		{
			if( resourceData )
			{
				if( resourceData.hasDefinition( className ) )	
				{
					var cls : Class	=	resourceData.getDefinition( className ) as Class;
					GTweener.TweenLite.delayedCall( 1, func, [name, className, cls].concat( params ), true );
				}
				else
					trace("SResourceLoader::getClassFuncEx class '"+className+"' not found" );
			}
			
			else
			{
				var targetObj : Object	=	new Object();
				targetObj.className		=	className;
				targetObj.target		=	func;
				targetObj.type			=	CALLBACK_FUNCTION_EX;
				targetObj.thisObject	=	thisObject;
				targetObj.params		=	params;
				
				targetList.push( targetObj );
				
				loadResource();
			}
		}		
			
		public function get isLoaded() : Boolean
		{
			return resourceData != null;
		}		
		
		public function get resourceName() : String
		{
			return name;
		}
		
		public function get resourcePriority() : String
		{
			return priority;
		}
		
		public function get resourceURL() : String
		{
			return resURL;
		}
		
		public function get resourceTotalByte() : int
		{
			return totalByte;
		}
		
		public function get resourceLoadedByte() : int
		{
			return loadedByte;
		}
		
		public function clearResourceRequest( obj : IResources ) : void
		{
			for( var i:int=0; i<targetList.length; i++ )
			{
				if( targetList[i].target == obj )
				{
					targetList.splice( i, 1 );
					break;
				}
			}
		}
		
		////////////////////////////////////////////////////////
		// Private methods
		////////////////////////////////////////////////////////
		
		/**
		 * 
		 * @param evt
		 * 
		 */		
		private function onSWFLoadComplete( evt : Event ) : void
		{
			resourceData	=	evt.currentTarget.applicationDomain;
			
			for each( var tarObj : Object in targetList )
			{
				if( resourceData.hasDefinition( tarObj.className ) )
				{
					var cls : Class	=	resourceData.getDefinition( tarObj.className ) as Class;
					
					if( tarObj.type == CALLBACK_OBJECT )
						tarObj.target.onLoadComplete( name, tarObj.className, cls );
					
					else if( tarObj.type == CALLBACK_FUNCTION )
					{
						if( tarObj.thisObject != null )
							tarObj.target.call( tarObj.thisObject, name, tarObj.className, cls );
						else
							tarObj.target( name, tarObj.className, cls );
					}
					
					else if( tarObj.type == CALLBACK_FUNCTION_EX )
					{
						if( tarObj.thisObject != null )
							tarObj.target.apply( tarObj.thisObject, [name, tarObj.className, cls].concat( tarObj.params ) );
						else
							tarObj.target.apply( null, [name, tarObj.className, cls].concat( tarObj.params ) );
					}
				}
				
				else
					trace( "ResourceLoader: Resource class '" + tarObj.className + "' NOT FOUND! At URL: " + resURL + " - Bucket name: " + name );
			}
						
			dispatchEvent( new Event( LOAD_COMPLETED ) );
						
			targetList	=	new Array();
			isLoading	=	false;
			
			//
			if( onLoadResultFunc != null )
				onLoadResultFunc.call( null, true, true, this, name, totalByte, loadedByte );
		}
		
		private function onLoadPercentage( evt:ProgressEvent ) : void
		{
			totalByte 	=	evt.bytesTotal;
			loadedByte	=	evt.bytesLoaded;
			
			if( onLoadResultFunc != null )
				onLoadResultFunc.call( null, true, false, this, name, totalByte, loadedByte );
		}
		
		private function onSWFLoadSecurityError( evt : SecurityErrorEvent ) : void
		{
			trace( "ResourceManager::SWF load security error: " + evt.text );
			
			// Retry to download resource
			isLoading	=	false;
			loadResource();
		}
		
		private function onNetStatusHandler(event:NetStatusEvent) : void 
		{
			switch( event.info.code )
			{
				case "NetConnection.Call.BadVersion":
					
					trace( "ResourceManager::NetConnection.Call.BadVersion " + event.info.code );
					isLoading	=	false;
					loadResource();
					
					break;
				
				case "NetConnection.Call.Failed":
					trace( "ResourceManager::NetConnection.Call.Failed " + event.info.code );
					
					isLoading	=	false;
					loadResource();
					
					break;
			}
		}
					
		private function onSWFLoadError( evt : IOErrorEvent ) : void
		{
			trace( "ResourceManager::SWF Load Error: " + evt.text );
					
			// Retry to download resource
			isLoading	=	false;
			setTimeout( loadResource, 1000 );
		}
	}
}